﻿using Manon.Autofac;
using Manon.Core.Commons;
using Manon.Gateway.ApplicationCore.Dtos;
using Manon.Gateway.ApplicationCore.Dtos.Enums;
using Manon.Gateway.ApplicationCore.Dtos.Routes;
using Manon.Gateway.ApplicationCore.Entities;
using Manon.Gateway.ApplicationCore.Infrastructure;
using Manon.Gateway.ApplicationCore.Interfaces;
using Manon.Repository;
using Manon.Repository.Collections;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Manon.Gateway.ApplicationCore.Services
{
    public class RouteService : IRouteService, ISingletonDependency
    {
        /// <summary>
        /// 获取项目分页列表
        /// </summary>
        /// <param name="Input"></param>
        /// <returns></returns>
        public async Task<IPagedList<GetProjectPageListOutput>> GetProjectPageList(GetProjectPageListInput Input)
        {
            var logger = Resolver.Resolve<ILogger<RouteService>>();
            try
            {
                var unitOfWork = Resolver.Resolve<IUnitOfWork<GatewayDbContext>>();
                Expression<Func<Project, bool>> predicate = null;
                if (Input.Enabled.HasValue)
                {
                    predicate = n => n.Enabled == Input.Enabled.Value;
                }
                if (!string.IsNullOrEmpty(Input.ProjectName))
                {
                    predicate = n => n.ProjectName.Contains(Input.ProjectName);
                }
                return await unitOfWork.GetRepository<Project>().GetPagedListAsync<GetProjectPageListOutput>(predicate, Input);

            }
            catch (Exception ex)
            {
                logger.LogError("GetProjectPageList异常", ex);
                throw;
            }
        }

        /// <summary>
        /// 获取项目信息
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<GetProjectOutput> GetProject(int Id)
        {
            var logger = Resolver.Resolve<ILogger<RouteService>>();
            try
            {
                var unitOfWork = Resolver.Resolve<IUnitOfWork<GatewayDbContext>>();
                var model = await unitOfWork.GetRepository<Project>().GetFirstOrDefaultAsync<GetProjectOutput>(n => n.Id == Id);
                if (model == null)
                {
                    return new GetProjectOutput()
                    {
                        Enabled = true,
                        OrderIndex = 1,
                        ProjectName = ""
                    };
                }
                return model;

            }
            catch (Exception ex)
            {
                logger.LogError("GetProject异常", ex);
                throw;
            }
        }

        /// <summary>
        /// 设置项目启用/禁用
        /// </summary>
        /// <param name="Id"></param>
        /// <param name="Enabled"></param>
        /// <returns></returns>
        public async Task<Result> SetProjectEnabled(int Id, bool Enabled)
        {
            var logger = Resolver.Resolve<ILogger<RouteService>>();
            try
            {
                var unitOfWork = Resolver.Resolve<IUnitOfWork<GatewayDbContext>>();
                var repos = unitOfWork.GetRepository<Project>();
                var result = await repos.GetFirstOrDefaultWithTrackingAsync(n => n.Id == Id);

                if (result == null) return Result.Failed("该项目不存在");

                result.Enabled = !result.Enabled;
                repos.Update(result);
                await unitOfWork.SaveChangesAsync();
                return Result.Successed("设置成功!");
            }
            catch (Exception ex)
            {
                logger.LogError("SetProjectEnabled异常", ex);
                throw;
            }
        }


        /// <summary>
        /// 保存项目
        /// </summary>
        /// <param name="Input"></param>
        /// <returns></returns>
        public async Task<Result> SaveProject(SaveProjectInput Input)
        {
            var logger = Resolver.Resolve<ILogger<RouteService>>();
            try
            {
                var unitOfWork = Resolver.Resolve<IUnitOfWork<GatewayDbContext>>();

                var repos = unitOfWork.GetRepository<Project>();
                var result = await repos.GetFirstOrDefaultAsync(n => n.Id == Input.Id);
                if (result == null)
                {
                    result = Input.ConvertTo<SaveProjectInput, Project>();
                    result.CreateTime = DateTime.Now;
                    result.UpdateTime = DateTime.Now;
                    await repos.InsertAsync(result);

                }
                else
                {
                    var newProject = Input.ConvertTo<SaveProjectInput, Project>();
                    newProject.Id = Input.Id;
                    newProject.CreateTime = result.CreateTime;
                    newProject.UpdateTime = DateTime.Now;
                    repos.Update(newProject);
                }

                await unitOfWork.SaveChangesAsync();
                return Result.Successed("保存成功!");

            }
            catch (Exception ex)
            {
                logger.LogError("保存项目异常", ex);
                throw;
            }
        }


        /// <summary>
        /// 保存路由设置
        /// </summary>
        /// <param name="Input"></param>
        /// <returns></returns>
        public async Task<Result> SaveRoute(SaveRouteInput Input)
        {
            var logger = Resolver.Resolve<ILogger<RouteService>>();
            try
            {
                var unitOfWork = Resolver.Resolve<IUnitOfWork<GatewayDbContext>>();
                using (var trans = unitOfWork.BeginTransaction())
                {
                    int RouteId = 0;
                    var repos = unitOfWork.GetRepository<Routes>();
                    var result = await repos.GetFirstOrDefaultAsync(n => n.Id == Input.Id);
                    if (result == null)
                    {
                        result = Input.ConvertTo<SaveRouteInput, Routes>();
                        result.Authentication_AllowedScopes = Input.Authentication_AllowedScopes_List == null ? "" : string.Join(",", Input.Authentication_AllowedScopes_List);
                        result.RateLimit_WhiteList = Input.RateLimit_WhiteList_list == null ? "" : string.Join(",", Input.RateLimit_WhiteList_list);
                        result.UpstreamHttpMethod = Input.UpstreamHttpMethodList == null ? "" : string.Join(",", Input.UpstreamHttpMethodList);

                        await repos.InsertAsync(result);
                        RouteId = result.Id;
                    }
                    else
                    {

                        var newRoutes = Input.ConvertTo<SaveRouteInput, Routes>();
                        newRoutes.Id = result.Id;
                        newRoutes.Authentication_AllowedScopes = Input.Authentication_AllowedScopes_List == null ? "" : string.Join(",", Input.Authentication_AllowedScopes_List);
                        newRoutes.RateLimit_WhiteList = Input.RateLimit_WhiteList_list == null ? "" : string.Join(",", Input.RateLimit_WhiteList_list);
                        newRoutes.UpstreamHttpMethod = Input.UpstreamHttpMethodList == null ? "" : string.Join(",", Input.UpstreamHttpMethodList);

                        repos.Update(newRoutes);
                        RouteId = newRoutes.Id;
                    }
                    await unitOfWork.SaveChangesAsync();

                    DeleteRouteProps(unitOfWork, RouteId);

                    await SaveRouteProps(unitOfWork, RouteId, Input.Claims, RoutePropsTypeEnum.Claims);

                    await SaveRouteProps(unitOfWork, RouteId, Input.Headers, RoutePropsTypeEnum.Header);
                    await SaveRouteProps(unitOfWork, RouteId, Input.HeaderUpTransforms, RoutePropsTypeEnum.HeaderUpTransforms);
                    await SaveRouteProps(unitOfWork, RouteId, Input.HeaderDownTransform, RoutePropsTypeEnum.HeaderDownTransform);
                    await SaveRouteProps(unitOfWork, RouteId, Input.RouteClaims, RoutePropsTypeEnum.RouteClaims);
                    await SaveRouteProps(unitOfWork, RouteId, Input.Queries, RoutePropsTypeEnum.Queries);
                    await SaveRouteProps(unitOfWork, RouteId, Input.DownTempletes, RoutePropsTypeEnum.DownTempletes);
                    await SaveHostPort(unitOfWork, RouteId, Input.HostPort);

                    await unitOfWork.SaveChangesAsync();
                    await trans.CommitAsync();
                    return Result.Successed("保存成功!");
                }
            }
            catch (Exception ex)
            {
                logger.LogError("保存路由设置异常", ex);
                throw;
            }
        }

        private void DeleteRouteProps(IUnitOfWork<GatewayDbContext> unitOfWork, int RouteId)
        {

            var repos = unitOfWork.GetRepository<RouteProperties>();
            var props = repos.GetAllByWhere(n => n.RouteId == RouteId);
            repos.Delete(props, true);
        }


        private async Task SaveRouteProps(IUnitOfWork<GatewayDbContext> unitOfWork, int RouteId, List<SavePropsInput> list, RoutePropsTypeEnum propType)
        {
            if (list == null || list.Count == 0) return;
            var repos = unitOfWork.GetRepository<RouteProperties>();
            foreach (var item in list)
            {
                await repos.InsertAsync(new RouteProperties()
                {
                    Key = item.Key,
                    Value = item.Value,
                    Type = (int)propType,
                    RouteId = RouteId
                });
            }
        }


        private async Task SaveHostPort(IUnitOfWork<GatewayDbContext> unitOfWork, int RouteId, List<SaveHostPortInput> list)
        {

            var repos = unitOfWork.GetRepository<RouteHostPort>();
            var props = repos.GetAllByWhere(n => n.RouteId == RouteId);
            repos.Delete(props, true);
            if (list == null || list.Count == 0) return;

            foreach (var item in list)
            {
                await repos.InsertAsync(new RouteHostPort()
                {
                    Host = item.Host,
                    Port = item.Port,
                    RouteId = RouteId
                });
            }
        }


        /// <summary>
        /// 获取路由信息
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<GetRoutesOutput> GetRoutes(int Id)
        {
            var logger = Resolver.Resolve<ILogger<RouteService>>();
            try
            {
                var unitOfWork = Resolver.Resolve<IUnitOfWork<GatewayDbContext>>();
                using (var trans = unitOfWork.BeginTransaction())
                {

                    var repos = unitOfWork.GetRepository<Routes>();
                    var result = await repos.GetFirstOrDefaultAsync<GetRoutesOutput>(n => n.Id == Id);
                    if (result == null) return new GetRoutesOutput() { };

                    var reposHostPort = unitOfWork.GetRepository<RouteHostPort>();
                    var reposProps = unitOfWork.GetRepository<RouteProperties>();
                    var props = await reposProps.GetAllByWhere(n => n.RouteId == result.Id).ToListAsync();
                    result.Headers = props?.Where(n => n.Type == (int)RoutePropsTypeEnum.Header).ToList().ConvertTo<RouteProperties, GetPropsOutput>();
                    result.HeaderUpTransforms = props?.Where(n => n.Type == (int)RoutePropsTypeEnum.HeaderUpTransforms).ToList().ConvertTo<RouteProperties, GetPropsOutput>();
                    result.HeaderDownTransform = props?.Where(n => n.Type == (int)RoutePropsTypeEnum.HeaderDownTransform).ToList().ConvertTo<RouteProperties, GetPropsOutput>();
                    result.Claims = props?.Where(n => n.Type == (int)RoutePropsTypeEnum.Claims).ToList().ConvertTo<RouteProperties, GetPropsOutput>();
                    result.RouteClaims = props?.Where(n => n.Type == (int)RoutePropsTypeEnum.RouteClaims).ToList().ConvertTo<RouteProperties, GetPropsOutput>();
                    result.Queries = props?.Where(n => n.Type == (int)RoutePropsTypeEnum.Queries).ToList().ConvertTo<RouteProperties, GetPropsOutput>();
                    result.DownTempletes = props?.Where(n => n.Type == (int)RoutePropsTypeEnum.DownTempletes).ToList().ConvertTo<RouteProperties, GetPropsOutput>();

                    result.HostPort = (await reposHostPort.GetAllByWhere(n => n.RouteId == result.Id).ToListAsync()).ConvertTo<RouteHostPort, GetHostPortOutput>(); ;
                    return result;
                }
            }
            catch (Exception ex)
            {
                logger.LogError("保存路由设置异常", ex);
                throw;
            }
        }


        /// <summary>
        /// 获取项目路由分页列表
        /// </summary>
        /// <param name="Input"></param>
        /// <returns></returns>
        public async Task<IPagedList<GetProjectRoutesPageListOutput>> GetProjectRoutesPageList(GetProjectRoutesPageListInput Input)
        {
            var logger = Resolver.Resolve<ILogger<RouteService>>();
            try
            {
                var unitOfWork = Resolver.Resolve<IUnitOfWork<GatewayDbContext>>();
                Expression<Func<Routes, bool>> predicate = n => n.ProjectId == Input.ProjectId;
                if (Input.Enabled.HasValue)
                {
                    predicate = n => n.Enabled == Input.Enabled.Value;
                }
                if (!string.IsNullOrEmpty(Input.Keyword))
                {
                    predicate = n => n.DownstreamPathTemplate.Contains(Input.Keyword)
                        || n.RequestIdKey.Contains(Input.Keyword)
                        || n.ServiceName.Contains(Input.Keyword)
                        || n.ServiceNamespace.Contains(Input.Keyword)
                        || n.UpstreamPathTemplate.Contains(Input.Keyword);
                }


                return await unitOfWork.GetRepository<Routes>().GetPagedListAsync<GetProjectRoutesPageListOutput>(predicate, Input);

            }
            catch (Exception ex)
            {
                logger.LogError("GetProjectRoutesPageList异常", ex);
                throw;
            }
        }



        /// <summary>
        /// 获取所有路由
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public async Task<List<GetAllRoutesOutput>> GetAllRoutes()
        {
            var logger = Resolver.Resolve<ILogger<RouteService>>();
            try
            {
                var unitOfWork = Resolver.Resolve<IUnitOfWork<GatewayDbContext>>();

                var projects = await unitOfWork.GetRepository<Project>().GetAllByWhere(n => n.Enabled == true).ToListAsync();
                var projectIds = projects.Select(n => n.Id).ToList();

                var models = await unitOfWork.GetRepository<Routes>().GetAllByWhere(n => n.Enabled == true && projectIds.Contains(n.ProjectId)).ToListAsync();
                if (models == null)
                {
                    return null;
                }

                List<int> listRoutesIds = models.Select(n => n.Id).ToList();

                var results = models.ConvertTo<Routes, GetAllRoutesOutput>(); ;

                var reposHostPort = unitOfWork.GetRepository<RouteHostPort>();
                var reposProps = unitOfWork.GetRepository<RouteProperties>();
                var props = (await reposProps.GetAllByWhere(n => listRoutesIds.Contains(n.RouteId)).ToListAsync());
                var hostPorts = (await reposHostPort.GetAllByWhere(n => listRoutesIds.Contains(n.RouteId)).ToListAsync()).ConvertTo<RouteHostPort, GetHostPortOutput>();
                foreach (var item in results)
                {
                    item.Headers = PropsToDictionary(props?.Where(n => n.RouteId == item.Id && n.Type == (int)RoutePropsTypeEnum.Header).ToList());
                    item.HeaderUpTransforms = PropsToDictionary(props?.Where(n => n.RouteId == item.Id && n.Type == (int)RoutePropsTypeEnum.HeaderUpTransforms).ToList());
                    item.HeaderDownTransform = PropsToDictionary(props?.Where(n => n.RouteId == item.Id && n.Type == (int)RoutePropsTypeEnum.HeaderDownTransform).ToList());
                    item.Claims = PropsToDictionary(props?.Where(n => n.RouteId == item.Id && n.Type == (int)RoutePropsTypeEnum.Claims).ToList());
                    item.RouteClaims = PropsToDictionary(props?.Where(n => n.RouteId == item.Id && n.Type == (int)RoutePropsTypeEnum.RouteClaims).ToList());
                    item.Queries = PropsToDictionary(props?.Where(n => n.RouteId == item.Id && n.Type == (int)RoutePropsTypeEnum.Queries).ToList());
                    item.DownTempletes = PropsToDictionary(props?.Where(n => n.RouteId == item.Id && n.Type == (int)RoutePropsTypeEnum.DownTempletes).ToList());

                    item.HostPort = hostPorts?.Where(n => n.RouteId == item.Id).ToList();
                }
                return results;

            }
            catch (Exception ex)
            {
                logger.LogError("获取所有路由异常", ex);
                throw;
            }
        }

        private Dictionary<string, string> PropsToDictionary(List<RouteProperties> list)
        {
            if (list == null || list.Count == 0) return null;

            Dictionary<string, string> dic = new Dictionary<string, string>();
            foreach (var item in list)
            {
                if (dic.ContainsKey(item.Key)) continue;

                dic.Add(item.Key, item.Value);
            }
            return dic;
        }
    }
}
